﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Xml;
using pub.Functions;
using todo.Model;

namespace todo.Persistence
{
    public class TodoListReader
    {
        public static TodoList Read(string fileName)
        {
            var todoList = new TodoList();

            var doc = new XmlDocument();
            try
            {
                doc.Load(fileName);
            }
            catch (Exception e)
            {
                var text = File.ReadAllText(fileName).Trim();
                if (text.Length == 0)
                {
                    todoList.Todos = new List<Todo>();
                    return todoList;
                }
                else
                {
                    throw;
                }
            }
            var todoListEl = doc.DocumentElement;
            var docChildren = GetSubElements(todoListEl);

            XmlElement elem;
            if (docChildren.TryGetValue("title", out elem))
            {
                todoList.Title = elem.GetText();
            }
            if (docChildren.TryGetValue("author", out elem))
            {
                todoList.Author = elem.GetText();
            }
            if (docChildren.TryGetValue("comments", out elem))
            {
                todoList.Comments = elem.GetText();
            }
            if(docChildren.TryGetValue("updateTime", out elem))
            {
                try
                {
                    todoList.UpdateTime = DateTime.ParseExact(elem.GetText(),
                            "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
                }
                catch(Exception e)
                {
                    Debug.Print("err: " + e.Message);
                }
            }

            var id = todoListEl.GetAttribute("id");
            todoList.Id = id;

            //
            var todos = ReadTodos(docChildren["todos"]);
            todoList.Todos = todos;

            return todoList;
        }

        private static List<Todo> ReadTodos(XmlElement todosElem)
        {
            var list = new List<Todo>();
            foreach (XmlElement todoElem in todosElem.ChildNodes)
            {
                list.Add(ReadTodo(todoElem));
            }
            return list;
        }

        private static Todo ReadTodo(XmlElement todoElem)
        {
            var todo = new Todo();
            var subElemMap = GetSubElements(todoElem);
            XmlElement elem;
            if (subElemMap.TryGetValue("title", out elem))
            {
                todo.Title = elem.InnerText;
            }
            if (subElemMap.TryGetValue("progress", out elem))
            {
                int progress;
                int.TryParse(elem.InnerText, out progress);
                todo.Progress = progress;
            }
            if (subElemMap.TryGetValue("due", out elem))
            {
                todo.Due = elem.InnerText;
            }
            if (subElemMap.TryGetValue("description", out elem))
            {
                todo.Description = elem.InnerText;
            }
            if (subElemMap.TryGetValue("todos", out elem))
            {
                todo.Todos = ReadTodos(elem);
            }
            if (subElemMap.TryGetValue("checked", out elem))
            {
                int checked_;
                int.TryParse(elem.InnerText.Trim(), out checked_);
                todo.Checked = checked_;
            }
            if (subElemMap.TryGetValue("active", out elem))
            {
                int active;
                int.TryParse(elem.InnerText.Trim(), out active);
                todo.Active = active;
            }

            var sId = todoElem.GetAttribute("id");
            if (sId.Length > 0)
            {
                todo.Id = sId;
            }

            var sDisplayStyle = todoElem.GetAttribute("displayStyle");
            if (sDisplayStyle.Length > 0)
            {
                todo.DisplayStyle = (DisplayStyle) Enum.Parse(typeof (DisplayStyle), sDisplayStyle);
            }
            var sCreateTime = todoElem.GetAttribute("createTime");
            if (!string.IsNullOrEmpty(sCreateTime))
            {
                todo.CreateTime = DateTime.ParseExact(sCreateTime,
                        "yyyyMMddHHmm", CultureInfo.InvariantCulture);
            }

            var sCheckTime = todoElem.GetAttribute("checkTime");
            if (!string.IsNullOrEmpty(sCheckTime))
            {
                todo.CheckTime = DateTime.ParseExact(sCheckTime,
                        "yyyyMMddHHmm", CultureInfo.InvariantCulture);
            }

            var color = todoElem.GetAttribute("color");
            if (color.Length > 0)
            {
                todo.Color = color;
            }

            var sPriority = todoElem.GetAttribute("priority");
            if (sPriority.Length > 0)
            {
                todo.Priority = int.Parse(sPriority);
            }

            //
            if (subElemMap.TryGetValue("reminders", out elem))
            {
                todo.Reminders = ReadReminders(elem);
            }

            //
            if (subElemMap.TryGetValue("tags", out elem))
            {
                todo.Tags = ReadTags(elem);
            }


            return todo;
        }

        private static List<string> ReadTags(XmlElement elem)
        {
            var tags = elem.GetText().Split(' ');
            return new List<string>(tags);
        }

        private static List<Reminder> ReadReminders(XmlElement elem)
        {
            var reminders = new List<Reminder>();
            for (var n = 0; n < elem.ChildNodes.Count; n++)
            {
                var reminderElem = elem.ChildNodes[n];
                reminders.Add(ReadReminder(reminderElem));
            }
            return reminders;
        }

        private static Reminder ReadReminder(XmlNode elem)
        {
            Reminder reminder;

            var sType = elem["type"].GetText();
            var type = (ReminderType) Enum.Parse(typeof (ReminderType), sType, true);


            if (type == ReminderType.Email)
            {
                var emailReminder = new EmailReminder();

                var to = elem["to"].GetText();
                emailReminder.To = to;

                reminder = emailReminder;
            }
            else
            {
                reminder = new Reminder();
            }
            reminder.Type = type;

            var timingElem = elem["timing"];

            var sTimingType = timingElem.GetAttribute("type");
            var timingType = (TimingType) Enum.Parse(typeof (TimingType), sTimingType, true);
            var timing = new Timing();
            timing.Type = timingType;
            timing.Value = timingElem.GetText();
            reminder.Timing = timing;

            return reminder;
        }

        private static IDictionary<string, XmlElement> GetSubElements(XmlElement parentElement)
        {
            var map = new Dictionary<string, XmlElement>();
            var nodes = parentElement.ChildNodes;
            foreach (XmlNode node in nodes)
            {
                map[node.Name] = node as XmlElement;
            }
            return map;
        }
    }
}